package lk;

public class D231007 {
    // 数组中的第K个最大元素


    public static void main(String[] args) {
        System.out.println(new D231007().findKthLargest(new int[]{6, 1, 2, 7, 9, 3, 4, 5, 10, 8}, 5));
    }

    // 基于快排
    // 时间：O(n)
    // 空间：O(logn)
    public int findKthLargest(int[] _nums, int k) {
        int n = _nums.length;
        return quickselect(_nums, 0, n - 1, n - k);
    }

    /**
     * 查找第 k 大的元素
     * @param nums  带查找数组
     * @param l     左边界下标
     * @param r     右边界下标
     * @param k     带查找元素的位置(第k大的元素)
     * @return      返回带查找元素
     */
    int quickselect(int[] nums, int l, int r, int k) {
        if (l == r) return nums[k];

        int x = nums[l];
        int i = l - 1;
        int j = r + 1;

        while (i < j) {
            do i++; while (nums[i] < x);
            do j--; while (nums[j] > x);
            if (i < j){
                int tmp = nums[i];
                nums[i] = nums[j];
                nums[j] = tmp;
            }
        }

        if (k <= j) return quickselect(nums, l, j, k);
        else return quickselect(nums, j + 1, r, k);
    }


    // 数组中的第K个最大元素
    // 基于堆排序
    // 时间：O(nlogn)
    // 空间：O(logn)
    public int findKthLargest2(int[] nums, int k) {
        int heapSize = nums.length;
        // 建立大根堆
        buildMaxHeap(nums, heapSize);

        // 通过交换来删除 k - 1 个堆顶元素, 然后第 k 大的元素就在堆顶了
        for (int i = nums.length - 1; i >= nums.length - k + 1; --i) {
            // 堆顶元素交换到尾部
            swap(nums, 0, i);
            // 堆的容量 -1
            --heapSize;
            // 向下调整
            maxHeapify(nums, 0, heapSize);
        }
        return nums[0];
    }

    // 向下调整建堆
    public void buildMaxHeap(int[] a, int heapSize) {
        // 从最后一个非叶子节点开始, 遍历到根结点
        // 向下调整
        for (int i = heapSize / 2; i >= 0; --i) {
            maxHeapify(a, i, heapSize);
        }
    }

    // 向下调整
    public void maxHeapify(int[] a, int i, int heapSize) {
        int l = i * 2 + 1;  // 左孩子
        int r = i * 2 + 2;   // 右孩子
        int largest = i;    // 父节点
        if (l < heapSize && a[l] > a[largest]) {
            largest = l;
        }
        if (r < heapSize && a[r] > a[largest]) {
            largest = r;
        }
        if (largest != i) {
            // 将最大值, 调整到父节点
            swap(a, i, largest);
            // 继续向下调整交换后的孩子节点
            maxHeapify(a, largest, heapSize);
        }
    }

    public void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

}
